package encaps.security;

import encaps.entities.Role;
import encaps.resources.Labels;
import encaps.utils.DataAccessException;
import encaps.utils.DatabaseUtil;
import encaps.utils.InitializationException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.LinkedList;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class RoleManager {

    private DataSource dataSource;
    private PreparedStatement ps;
    private RoleCache cache;
    private static RoleManager roleManager;

    private RoleManager() throws InitializationException {
        try {
            dataSource = DatabaseUtil.getDataSource();
            cache = new RoleCache();
        } catch (NamingException ex) {
            throw new InitializationException(Labels.getLabel(Labels.ROLE_MANAGER_ININ_ERROR_MESSAGE), ex);
        }
    }

    //Singleton - один RoleManager на всю систему
    public static RoleManager getRoleManager() throws InitializationException {
        if (roleManager == null) {
            roleManager = new RoleManager();
        }
        return roleManager;
    }

    public synchronized Role getRole(String roleName) throws DataAccessException {
        return cache.get(roleName);
    }

    /*
     * public Role loadRole(String name) throws DataAccessException { Connection
     * conn = null; try { conn = dataSource.getConnection(); ps =
     * conn.prepareStatement("SELECT name FROM role WHERE name=?");
     * ps.setString(1, name); ResultSet rs = ps.executeQuery(); if(!rs.next()) {
     * throw new DataAccessException("Role + " + name + " not found"); }
     * DatabaseUtil.closeConnection(null, ps);
     *
     * ps = conn.prepareStatement("SELECT grant_name FROM role_grant WHERE
     * role_name=?"); ps.setString(1, name); rs = ps.executeQuery(); Role r =
     * new Role(name); while(rs.next()) { r.addGrant(new
     * Grant(rs.getString("grant_name"))); } return r; } catch(SQLException ex)
     * { throw new DataAccessException("Exception while accessing database",
     * ex); } finally { DatabaseUtil.closeConnection(conn, ps); } }
     */
    public synchronized void updateRole(String oldname, Role r) throws DataAccessException {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement("SELECT name FROM role WHERE name=?");
            ps.setString(1, oldname);
            ResultSet rs = ps.executeQuery();
            if (!rs.next()) {
                throw new DataAccessException(Labels.getLabel(Labels.ROLE_NOT_FOUND_MESSAGE));
            }
            ps = conn.prepareStatement("DELETE FROM role_grant WHERE role_name=?");
            ps.setString(1, oldname);
            if (ps.executeUpdate() != 1) {
                throw new SQLException(Labels.getLabel(Labels.DELETE_ERROR_MESSAGE));
            }
            if (!oldname.equals(r.getRoleName())) {
                ps = conn.prepareStatement("UPDATE role SET name=? WHERE name=?");
                ps.setString(1, oldname);
                ps.setString(1, r.getRoleName());
                if (ps.executeUpdate() != 1) {
                    throw new SQLException(Labels.getLabel(Labels.UPDATE_ERROR_MESSAGE));
                }
            }
            ps = conn.prepareStatement("INSERT INTO role_grant(role_name,grant_name) VALUES(?,?) ");
            String[] grants = r.getGrantsAsString().split(";");
            for (int i = 0; i < grants.length; i++) {
                ps.setString(1, r.getRoleName());
                ps.setString(2, grants[i]);
                if (ps.executeUpdate() != 1) {
                    throw new SQLException(Labels.getLabel(Labels.INSERT_ERROR_MESSAGE));
                }
            }
        } catch (SQLException ex) {
            throw new DataAccessException(Labels.getLabel(Labels.DATABASE_ACCESS_ERROR_MESSAGE), ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
        cache.reset();
    }

    public synchronized void storeRole(Role r) throws DataAccessException {
        Connection conn = null;
        try {
            if (isRoleExist(r.getRoleName())) {
                throw new DataAccessException(Labels.getLabel(Labels.ROLE_ALREADY_EXISTS_MESSAGE));
            }
            conn = dataSource.getConnection();
            ps = conn.prepareStatement("INSERT INTO role(name) VALUES(?)");
            ps.setString(1, r.getRoleName());
            if (ps.executeUpdate() != 1) {
                throw new SQLException(Labels.getLabel(Labels.INSERT_ERROR_MESSAGE));
            }

            ps = conn.prepareStatement("INSERT INTO role_grant(role_name,grant_name) VALUES(?,?) ");
            String[] grants = r.getGrantsAsString().split(";");
            for (int i = 0; i < grants.length; i++) {
                ps.setString(1, r.getRoleName());
                ps.setString(2, grants[i]);
                if (ps.executeUpdate() != 1) {
                    throw new SQLException(Labels.getLabel(Labels.INSERT_ERROR_MESSAGE));
                }
            }
        } catch (SQLException ex) {
            throw new DataAccessException(Labels.getLabel(Labels.DATABASE_ACCESS_ERROR_MESSAGE), ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
        cache.reset();
    }

    public synchronized Collection<Role> getAllRoles() throws DataAccessException {
        Connection conn = null;
        try {
            Collection<Role> roles = new LinkedList<Role>();
            conn = dataSource.getConnection();
            ps = conn.prepareStatement("SELECT name FROM role");
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                String roleName = rs.getString(1);
                Role role = new Role(roleName);
                roles.add(role);
            }
            return roles;
        } catch (SQLException ex) {
            throw new DataAccessException(Labels.getLabel(Labels.DATABASE_ACCESS_ERROR_MESSAGE), ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public synchronized boolean isRoleExist(String name) throws DataAccessException {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            ps = conn.prepareStatement("SELECT name FROM role WHERE name=?");
            ps.setString(1, name);
            ResultSet rs = ps.executeQuery();
            if (!rs.next()) {
                return false;
            } else {
                return true;
            }
        } catch (SQLException ex) {
            throw new DataAccessException(Labels.getLabel(Labels.DATABASE_ACCESS_ERROR_MESSAGE), ex);
        } finally {
            DatabaseUtil.closeConnection(conn, ps);
        }
    }

    public synchronized void clearCache() {
        cache.reset();
    }
}
